﻿/*
* Copyright 2009-2010 AKABANA.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
using System;
using System.Text;
using Akabana.Amf.Amf0.IO;
using Akabana.Amf.Exception;
using Akabana.Amf.IO;
using Akabana.Amf.Serializer;
using Akabana.Amf.Util;

namespace Akabana.Amf.Amf0.Serializer
{

    public class Amf0Deserializer : AbstractAmfDeserializer
    {

        public AbstractAmfDeserializer amfDeserializer = null;

        public Amf0Deserializer(AmfReader amfReader)
            : base(amfReader)
        {
        }

        public override object Deserialize()
        {
            this.referenceClear();
            object result = this.DoDecode();
            return result;
        }

        protected override IAmfReferenceTable CreateReferenceTable()
        {
            return new Amf0ReferenceTable();
        }

        protected object DoDecode()
        {
            object result = null;

            var typeMarker = amfReader.ReadTypeMarker();

            switch (typeMarker)
            {
                case Amf0TypeMarker.NumberMarker:
                    result = this.DoDecodeNumber();
                    break;
                case Amf0TypeMarker.BooleanMarker:
                    result = this.DoDecodeBoolean();
                    break;
                case Amf0TypeMarker.StringMarker:
                    result = this.DoDecodeString();
                    break;
                case Amf0TypeMarker.NullMarker:
                    result = null;
                    break;
                case Amf0TypeMarker.UndefinedMarker:
                    result = null;
                    break;
                case Amf0TypeMarker.StrictArrayMarker:
                    result = this.DoDecodeStrictArray();
                    break;
                case Amf0TypeMarker.DateMarker:
                    result = this.DoDecodeDate();
                    break;
                case Amf0TypeMarker.LongStringMarker:
                    result = this.DoDecodeLongString();
                    break;
                case Amf0TypeMarker.AvmplusObjectMarker:
                    result = this.DoDecodeAvmPlusObject();
                    break;

                case Amf0TypeMarker.ObjectMarker:
                case Amf0TypeMarker.ReferenceMarker:
                case Amf0TypeMarker.EcmaArrayMarker:
                case Amf0TypeMarker.ObjectEndMarker:
                case Amf0TypeMarker.UnsupportedMarker:
                case Amf0TypeMarker.XmlDocumentMarker:
                case Amf0TypeMarker.TypedObjectMarker:
                case Amf0TypeMarker.MovieclipMarker:
                case Amf0TypeMarker.RecordsetMarker:
                default:
                    throw new UnsupportTypeMarkerException((byte)typeMarker);
            }

            return result;
        }

        private double DoDecodeNumber()
        {
            return amfReader.ReadNumber();
        }

        private bool DoDecodeBoolean()
        {
            byte boolean = amfReader.ReadByte();
            bool result;
            if (boolean == 0)
            {
                result = false;
            }
            else
            {
                result = true;
            }
            return result;
        }

        private string DoDecodeString()
        {
            return amfReader.ReadUtfString();
        }

        private string DoDecodeLongString()
        {
            var stringSize = amfReader.ReadU32();
            var stringData = new byte[stringSize];
            amfReader.Read(stringData, 0, (int)stringSize);

            return Encoding.UTF8.GetString(stringData, 0, stringData.Length);
        }

        private DateTime DoDecodeDate()
        {
            var timeZone = amfReader.ReadS16();
            var time = (long)amfReader.ReadNumber();

            var result = DateUtil.ToUtcDateTime(time);

            return result;
        }

        private Array DoDecodeStrictArray()
        {
            var u32 = amfReader.ReadU32();
            var result = new object[u32];

            for (int i = 0; i < u32; i++)
            {
                result[i] = this.DoDecode();
            }

            return result;
        }

        private object DoDecodeAvmPlusObject()
        {
            if (amfDeserializer == null)
            {
                throw new UnsupportTypeException(Amf0Consts.AVMPLUS_OBJECT);
            }

            return this.amfDeserializer.Deserialize();

        }
    }
}